package services

import (
	"context"
	"fmt"
	"github.com/pkg/errors"
	"github.com/prometheus/common/model"
	"github.com/spf13/viper"
	"go.didapinche.com/foundation/plat-echo/app/ops/models"
	"go.didapinche.com/foundation/plat-echo/app/ops/repositories"
	promentheus "go.didapinche.com/foundation/plat-echo/pkg/prometheus"
	"go.uber.org/zap"
	"time"
)

type ApiService interface {
	GetApiByApp(ctx context.Context) error
	GetApiByZeus(ctx context.Context) error
	FindByID(ctx context.Context, id uint64) (*models.Api, error)
	Create(ctx context.Context, api *models.Api) (*models.Api, error)
	UpdateStatus(ctx context.Context, id uint64, disable_ids, enableIDS []int) error
	FindByZeusClient(ctx context.Context, apiID uint64) ([]*models.Api, error)
}

type apiService struct {
	logger          *zap.Logger
	graphRepository repositories.GraphRepository
	repository      repositories.ApiRepository
	prome           *promentheus.PrometheusService
	appRepository   repositories.ApplicationRepository
	k8s             K8sService
	templateSerivce TemplateService
}

func NewApiService(repository repositories.ApiRepository, graphRepository repositories.GraphRepository, logger *zap.Logger, prome *promentheus.PrometheusService, appRepository repositories.ApplicationRepository, k8s K8sService, templateSerivce TemplateService) ApiService {
	return &apiService{
		repository:      repository,
		prome:           prome,
		logger:          logger.With(zap.String("type", "services.apiService")),
		appRepository:   appRepository,
		k8s:             k8s,
		graphRepository: graphRepository,
		templateSerivce: templateSerivce,
	}
}

const httpQuery = "sum(rate(http_server_requests_seconds_count[1h])) by(app, uri,method)"
const zeusQuery = "sum(rate(zeus_rpc_seconds_count[1h])) by(app, service, method,kind)"

func (a apiService) FindByID(ctx context.Context, id uint64) (*models.Api, error) {
	api, err := a.repository.FindByID(ctx, id)
	if err != nil {
		return nil, err
	}
	if api.HttpMethod != "" {
		api.TemplateType = 1
	} else {
		api.TemplateType = 2
	}

	return api, nil
}

//获得http的api
func (s apiService) GetApiByApp(ctx context.Context) error {
	nodes := s.templateSerivce.GetNodeByGroup(ctx)
	apps, err := s.appRepository.FindAll(ctx)
	if err != nil {
		s.logger.Error("call appRepository FindAll error", zap.Error(err))
	}
	appMap := make(map[string]*models.Application)
	for _, app := range apps {
		appMap[app.Name] = app
	}
	result, err := s.prome.QueryVectorProm(viper.GetString("http.collect"), time.Now())
	if err != nil {
		s.logger.Error("call prome QueryVectorProm error", zap.Error(err))
	}
	apis, err := s.repository.FindAll(ctx)
	if err != nil {
		if err != nil {
			s.logger.Error("call repository FindAll error", zap.Error(err))
		}
	}
	m := make(map[string]int) //使用AppName:HttpMethod：HttpPath作为key
	for _, a := range apis {
		key := a.AppName + ":" + a.HttpMethod + ":" + a.HttpPath
		m[key] = 0
	}
	switch result.(type) {
	case model.Vector:
		{
			vector, ok := result.(model.Vector)
			if ok && len(vector) > 0 {
				for _, v := range vector {
					v.Metric[""] = ""
					api := &models.Api{
						AppName:    string(v.Metric["app"]),
						HttpPath:   string(v.Metric["uri"]),
						HttpMethod: string(v.Metric["method"]),
						Type:       0,
					}
					key := api.AppName + ":" + api.HttpMethod + ":" + api.HttpPath
					if _, ok := m[key]; !ok {
						if app, ok := appMap[api.AppName]; ok {
							api.AppID = app.ID
							api.DevGroupID = app.DevGroupID
							api.DevGroupName = app.DevGroupName
							api.Owner = app.Owner
							api.Level = app.Level
							api, err = s.repository.Create(ctx, api)
							if err != nil {
								s.logger.Error("call repository Create error", zap.Error(err))
							} else {
								templates, err := s.templateSerivce.FindByApiID(ctx, api.ID)
								if err != nil {
									s.logger.Error("call templateService FindByApiID error", zap.Error(err))
								}
								graphs := s.templateSerivce.ChangeTemplate(ctx, templates, api.ID, nodes)
								if err := s.graphRepository.Creates(ctx, graphs); err != nil {
									s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
								}
								//	s.templateSerivce.UpdateK8s(ctx, int(api.ID), 1, "api-"+strconv.Itoa(int(api.ID)))
							}
						}
					} else {
						s.logger.Error("skip a http")
					}
				}
			}
		}
	default:
	}
	return nil
}

//获得zeus的api
func (s apiService) GetApiByZeus(ctx context.Context) error {
	nodes := s.templateSerivce.GetNodeByGroup(ctx)
	apps, err := s.appRepository.FindAll(ctx)
	if err != nil {
		s.logger.Error("call appRepository FindAll error", zap.Error(err))
	}
	appMap := make(map[string]*models.Application)
	for _, app := range apps {
		appMap[app.Name] = app
	}
	result, err := s.prome.QueryVectorProm(viper.GetString("zeus.collect"), time.Now())
	if err != nil {
		s.logger.Error("call prome QueryVectorProm error", zap.Error(err))

	}
	apis, err := s.repository.FindAll(ctx)
	if err != nil {
		s.logger.Error("call repository FindAll error", zap.Error(err))

	}
	m := make(map[string]int) //使用AppName:HttpMethod：HttpPath作为key
	for _, a := range apis {
		key := a.AppName + ":" + a.ZeusService + ":" + a.ZeusMethod
		m[key] = 0
	}
	switch result.(type) {
	case model.Vector:
		{
			vector, ok := result.(model.Vector)
			if ok && len(vector) > 0 {
				for _, v := range vector {
					v.Metric[""] = ""
					api := &models.Api{
						AppName:     string(v.Metric["app"]),
						ZeusService: string(v.Metric["service"]),
						ZeusMethod:  string(v.Metric["method"]),
					}
					key := api.AppName + ":" + api.ZeusService + ":" + api.ZeusMethod
					if _, ok := m[key]; !ok {
						if app, ok := appMap[api.AppName]; ok {
							api.AppID = app.ID
							api.Owner = app.Owner
							api.Level = app.Level
							api.DevGroupID = app.DevGroupID
							api.DevGroupName = app.DevGroupName
							if v.Metric["kind"] == "server" {
								api.Type = 1
							} else {
								api.Type = 2
							}
							api, err = s.repository.Create(ctx, api)
							if err != nil {
								s.logger.Error("call repository Create error", zap.Error(err))
							} else {
								templates, err := s.templateSerivce.FindByApiID(ctx, api.ID)
								if err != nil {
									s.logger.Error("call templateService FindByApiID error", zap.Error(err))
								}
								graphs := s.templateSerivce.ChangeTemplate(ctx, templates, api.ID, nodes)
								if err := s.graphRepository.Creates(ctx, graphs); err != nil {
									s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
								}
								//s.templateSerivce.UpdateK8s(ctx, int(api.ID), 1, "api-"+strconv.Itoa(int(api.ID)))
							}
						} else {
							s.logger.Error("skip a zeus" + fmt.Sprint(api))
						}

					}
				}
			}
		}
	default:
	}
	return nil
}

func (s apiService) Create(ctx context.Context, api *models.Api) (*models.Api, error) {
	nodes := s.templateSerivce.GetNodeByGroup(ctx)
	api, err := s.repository.Create(ctx, api)
	if err != nil {
		s.logger.Error("call repository Create error", zap.Error(err))
		return nil, errors.Wrap(err, "call repository Create error")
	} else {
		templates, err := s.templateSerivce.FindByApiID(ctx, api.ID)
		if err != nil {
			s.logger.Error("call templateService FindByApiID error", zap.Error(err))
			return nil, errors.Wrap(err, "call templateService FindByApiID error")
		}
		graphs := s.templateSerivce.ChangeTemplate(ctx, templates, api.ID, nodes)
		if err := s.graphRepository.Creates(ctx, graphs); err != nil {
			s.logger.Error("call templateService RefreshTemplate error:s.graphRepository.Creates", zap.Error(err))
			return nil, errors.Wrap(err, "call templateService RefreshTemplate error:s.graphRepository.Creates")
		}
		//s.templateSerivce.UpdateK8s(ctx, int(api.ID), 1, "api-"+strconv.Itoa(int(api.ID)))
	}
	return api, nil
}

func (s apiService) UpdateStatus(ctx context.Context, id uint64, disable_ids, enableIDS []int) error {
	api, err := s.repository.FindByID(ctx, id)
	if err != nil || api == nil {
		s.logger.Error("call repository.FindByID error", zap.Error(err))
		return errors.Wrap(err, "call repository.FindByID error")
	}
	if len(enableIDS) > 0 {
		api.DisableGraph = &models.DisableGraph{GraphID: disable_ids}
	} else if api.DisableGraph == nil {
		api.DisableGraph = &models.DisableGraph{GraphID: disable_ids}
	} else {
		api.DisableGraph.GraphID = append(api.DisableGraph.GraphID, disable_ids...)
	}
	if err := s.repository.Update(ctx, api); err != nil {
		s.logger.Error("call repository.Save error", zap.Error(err))
		return errors.Wrap(err, "call repository.Save error")
	}
	return nil
}
func (s apiService) FindByZeusClient(ctx context.Context, apiID uint64) ([]*models.Api, error) {
	api, err := s.repository.FindByID(ctx, apiID)
	if err != nil {
		return nil, errors.Wrap(err, "call FindByZeusClient error")
	}
	apis, err := s.repository.FindByZeusClient(ctx, api.ZeusMethod, api.ZeusService)
	if err != nil {
		return nil, errors.Wrap(err, "call FindByZeusClient error")
	}
	return apis, nil
}
